﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;
using System.Web.UI.Design;
using System.Windows.Forms;

namespace DriveBeamEditor.Controls.General
{
    public partial class AdvSplitContainer : SplitContainer
    {
        #region Fields

        private Color _splitterBackColor1 = Color.AliceBlue;
        private Color _splitterBackColor2 = Color.Silver;
        private Color _splitterBackColor3 = Color.Gray;
        private Color _splitterHoverBackColor1 = Color.White;
        private Color _splitterHoverBackColor2 = Color.Gainsboro;
        private Color _splitterHoverBackColor3 = Color.Silver;
        private float _splitterGradientAngle = 0f;
        private PictureSize _buttonSize = PictureSize.Medium;
        private bool _mouseOver = false;
        private bool _isOverArrow1 = false;
        private bool _isOverArrow2 = false;
        private bool _isOverCenter = false;
        private Cursor _oldCursor = Cursors.Arrow;
        private Point _savedMousePos;
        private int _panel1MaxSize = 250;
        private int _panel2MaxSize = 250;
        private int _splitterResetDistance = -1;

        private const int SmallSize = 16;
        private const int MediumSize = 24;
        private const int LargeSize = 32;

        #endregion

        #region Constants

        public enum PictureSize
        {
            Small = 16,
            Medium = 24,
            Large = 32
        }

        #endregion

        #region Properties

        /// <summary>Gets or sets the button size</summary>
        public PictureSize ButtonSize
        {
            get { return _buttonSize; }
            set
            {
                if (value == _buttonSize) return;

                _buttonSize = value;
                SplitterWidth = SplitterMinWidth;

                Invalidate();
            }
        }

        public Color SplitterBackColor1
        {
            get { return _splitterBackColor1; }
            set
            {
                _splitterBackColor1 = value;
                Invalidate(SplitterRectangle);
            }
        }

        public Color SplitterBackColor2
        {
            get { return _splitterBackColor2; }
            set
            {
                _splitterBackColor2 = value;
                Invalidate(SplitterRectangle);
            }
        }

        public Color SplitterBackColor3
        {
            get { return _splitterBackColor3; }
            set
            {
                _splitterBackColor3 = value;
                Invalidate(SplitterRectangle);
            }
        }

        public Color SplitterHoverBackColor1
        {
            get { return _splitterHoverBackColor1; }
            set
            {
                _splitterHoverBackColor1 = value;
                Invalidate(SplitterRectangle);
            }
        }

        public Color SplitterHoverBackColor2
        {
            get { return _splitterHoverBackColor2; }
            set
            {
                _splitterHoverBackColor2 = value;
                Invalidate(SplitterRectangle);
            }
        }

        public Color SplitterHoverBackColor3
        {
            get { return _splitterHoverBackColor3; }
            set
            {
                _splitterHoverBackColor3 = value;
                Invalidate(SplitterRectangle);
            }
        }

        public float SplitterGradientAngle
        {
            get { return _splitterGradientAngle; }
            set
            {
                _splitterGradientAngle = value % 360;
                Invalidate(SplitterRectangle);
            }
        }

        protected int SplitImageIndex
        {
            get
            {
                return Orientation == Orientation.Vertical ? 4 : 5;
            }
        }

        protected int Panel1ImageIndex
        {
            get
            {
                return Orientation == Orientation.Vertical ? 2 : 0;
            }
        }

        protected int Panel2ImageIndex
        {
            get
            {
                return Orientation == Orientation.Vertical ? 3 : 1;
            }
        }

        protected RectangleF SplitImageRectangle
        {
            get
            {
                var x = Orientation == Orientation.Vertical ? SplitterDistance : ClientSize.Width * 0.5f - ImageSize * 0.5f;
                var y = Orientation == Orientation.Horizontal ? SplitterDistance : ClientSize.Height * 0.5f - ImageSize * 0.5f;
                return new RectangleF(x + 3f, y + 3f, ImageSize, ImageSize);
            }
        }

        protected RectangleF Panel1ImageRectangle
        {
            get
            {
                var x = Orientation == Orientation.Vertical ? SplitterDistance : ClientSize.Width * 0.25f - ImageSize * 0.5f;
                var y = Orientation == Orientation.Horizontal ? SplitterDistance : ClientSize.Height * 0.25f - ImageSize * 0.5f;
                return new RectangleF(x + 3f, y + 3f, ImageSize, ImageSize);
            }
        }

        protected RectangleF Panel2ImageRectangle
        {
            get
            {
                var x = Orientation == Orientation.Vertical ? SplitterDistance : ClientSize.Width * 0.75f - ImageSize * 0.5f;
                var y = Orientation == Orientation.Horizontal ? SplitterDistance : ClientSize.Height * 0.75f - ImageSize * 0.5f;
                return new RectangleF(x + 3f, y + 3f, ImageSize, ImageSize);
            }
        }

        protected int ImageSize
        {
            get
            {
                return (int)ButtonSize;
            }
        }

        protected int SplitterMinWidth
        {
            get
            {
                return (int)ButtonSize + 6;
            }
        }

        protected ImageList SplitImageList
        {
            get
            {
                switch (ButtonSize)
                {
                    case PictureSize.Small:
                        return _isOverCenter ? imagesSmallUp : imagesSmall;
                    case PictureSize.Medium:
                        return _isOverCenter ? ImagesMediumUp : imagesMedium;
                    case PictureSize.Large:
                        return _isOverCenter ? imagesLargeUp : imagesLarge;
                    default:
                        return _isOverCenter ? imagesLargeUp : imagesLarge;
                }
            }
        }

        protected ImageList Panel1ImageList
        {
            get
            {
                switch (ButtonSize)
                {
                    case PictureSize.Small:
                        return _isOverArrow1 ? imagesSmallUp : imagesSmall;
                    case PictureSize.Medium:
                        return _isOverArrow1 ? ImagesMediumUp : imagesMedium;
                    case PictureSize.Large:
                        return _isOverArrow1 ? imagesLargeUp : imagesLarge;
                    default:
                        return _isOverArrow1 ? imagesLargeUp : imagesLarge;
                }
            }
        }

        protected ImageList Panel2ImageList
        {
            get
            {
                switch (ButtonSize)
                {
                    case PictureSize.Small:
                        return _isOverArrow2 ? imagesSmallUp : imagesSmall;
                    case PictureSize.Medium:
                        return _isOverArrow2 ? ImagesMediumUp : imagesMedium;
                    case PictureSize.Large:
                        return _isOverArrow2 ? imagesLargeUp : imagesLarge;
                    default:
                        return _isOverArrow2 ? imagesLargeUp : imagesLarge;
                }
            }
        }

        public int Panel1MaxSize
        {
            get { return _panel1MaxSize; }
            set { _panel1MaxSize = value; }
        }

        public int Panel2MaxSize
        {
            get { return _panel2MaxSize; }
            set { _panel2MaxSize = value; }
        }

        public int SplitterResetDistance
        {
            get { return _splitterResetDistance; }
            set
            {
                if (value == _splitterResetDistance) return;
                _splitterResetDistance = value;
                Invalidate(SplitterRectangle);
            }
        }

        public RectangleF SplitterShadowRectangle
        {
            get
            {
                var x = Orientation == Orientation.Horizontal ? 0 : SplitterRectangle.X + SplitterWidth * 0.5f;
                var y = Orientation == Orientation.Vertical ? 0 : SplitterRectangle.Y + SplitterWidth * 0.5f;
                var w = Orientation == Orientation.Horizontal ? SplitterRectangle.Width : SplitterWidth * 0.5f;
                var h = Orientation == Orientation.Vertical ? SplitterRectangle.Height : SplitterWidth * 0.5f;
                return new RectangleF(x, y, w, h);
            }
        }

        public RectangleF SplitterShadowBrushRectangle
        {
            get
            {
                return Orientation == Orientation.Horizontal
                    ? new RectangleF(SplitterShadowRectangle.X, SplitterShadowRectangle.Y - 1f,
                        SplitterShadowRectangle.Width, SplitterShadowRectangle.Height + 2f)
                    : new RectangleF(SplitterShadowRectangle.X - 1f, SplitterShadowRectangle.Y,
                        SplitterShadowRectangle.Width, SplitterShadowRectangle.Height + 2f);
            }
        }

        #endregion

        #region CTORs

        public AdvSplitContainer()
        {
            InitializeComponent();
            SetStyle(
                ControlStyles.AllPaintingInWmPaint |
                ControlStyles.DoubleBuffer |
                ControlStyles.Opaque |
                ControlStyles.OptimizedDoubleBuffer |
                ControlStyles.ResizeRedraw |
                ControlStyles.UserPaint,
                true
            );
        }

        #endregion

        #region Events

        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);

            /* Configure graphics */
            pe.Graphics.CompositingQuality = CompositingQuality.HighQuality;
            pe.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

            /* Check if mouse is over arrows / center */
            var cur = PointToClient(Cursor.Position);
            _isOverCenter = _mouseOver & SplitImageRectangle.Contains(cur);
            _isOverArrow1 = _mouseOver & Panel1ImageRectangle.Contains(cur);
            _isOverArrow2 = _mouseOver & Panel2ImageRectangle.Contains(cur);

            /* Force splitter min width */
            if (SplitterWidth < SplitterMinWidth)
                SplitterWidth = SplitterMinWidth;

            /* Fill splitter with splitter backcolors */
            var color1 = _mouseOver ? SplitterHoverBackColor1 : SplitterBackColor1;
            var color2 = _mouseOver ? SplitterHoverBackColor2 : SplitterBackColor2;
            var color3 = _mouseOver ? SplitterHoverBackColor3 : SplitterBackColor3;
            var backBrush = new LinearGradientBrush(SplitterRectangle, color1, color2, SplitterGradientAngle);
            pe.Graphics.FillRectangle(backBrush, SplitterRectangle);
            var shadowBrush = new LinearGradientBrush(SplitterShadowBrushRectangle, Color.FromArgb(50, color2), color3, SplitterGradientAngle);
            pe.Graphics.FillRectangle(shadowBrush, SplitterShadowRectangle);

            /* Draw splitter image */
            var imgSplit = SplitImageList.Images[SplitImageIndex];
            pe.Graphics.DrawImage(imgSplit, SplitImageRectangle);

            /* Draw arrows */
            var imgPanel1 = Panel1ImageList.Images[Panel1ImageIndex];
            var imgPanel2 = Panel2ImageList.Images[Panel2ImageIndex];
            pe.Graphics.DrawImage(imgPanel1, Panel1ImageRectangle);
            pe.Graphics.DrawImage(imgPanel2, Panel2ImageRectangle);
        }

        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
            Pop();
        }

        private void AdvSplitContainer_SplitterMoved(object sender, SplitterEventArgs e)
        {
            Invalidate(SplitterRectangle);
        }

        private void AdvSplitContainer_SplitterMoving(object sender, SplitterCancelEventArgs e)
        {
            Invalidate(SplitterRectangle);
        }

        private void AdvSplitContainer_MouseEnter(object sender, EventArgs e)
        {
            _mouseOver = true;
            _oldCursor = Cursor;
            Cursor = Cursors.Hand;
            Invalidate(SplitterRectangle);
        }

        private void AdvSplitContainer_MouseLeave(object sender, EventArgs e)
        {
            _mouseOver = false;
            Cursor = _oldCursor;
            Invalidate(SplitterRectangle);
        }

        private void AdvSplitContainer_MouseDown(object sender, MouseEventArgs e)
        {
            _savedMousePos = e.Location;

            Invalidate(SplitterRectangle);
            if (_isOverArrow1)
                CollapsePanel1();
            else if (_isOverArrow2)
                CollapsePanel2();
            else if (_isOverCenter)
                Pop();
        }

        private void AdvSplitContainer_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Location != _savedMousePos) return;
        }

        private void AdvSplitContainer_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            var max = (Orientation == Orientation.Vertical ? Width - SplitterWidth : Height - SplitterWidth) - Panel2MinSize;
            SplitterDistance = SplitterDistance == max ? 0 : max;
        }

        private void AdvSplitContainer_MouseMove(object sender, MouseEventArgs e)
        {
            Invalidate(SplitterRectangle);
        }

        #endregion

        #region Methods

        public void ExpandPanel1()
        {
            SplitterDistance = _panel1MaxSize;
            Invalidate(SplitterRectangle);
        }

        public void ExpandPanel2()
        {
            SplitterDistance = (Orientation == Orientation.Vertical ? Width - SplitterWidth - _panel2MaxSize : Height - SplitterWidth - _panel2MaxSize);
            Invalidate(SplitterRectangle);
        }

        public void CollapsePanel1()
        {
            SplitterDistance = Panel1MinSize;
            Invalidate(SplitterRectangle);
        }

        public void CollapsePanel2()
        {
            SplitterDistance = (Orientation == Orientation.Vertical ? Width - SplitterWidth : Height - SplitterWidth) - Panel2MinSize;
            Invalidate(SplitterRectangle);
        }

        public void TogglePanel1()
        {
            var min = SplitterWidth + Panel1MinSize;
            var max = SplitterWidth + _panel1MaxSize;
            SplitterDistance = SplitterDistance == max ? min : max;
            Invalidate(SplitterRectangle);
        }

        public void TogglePanel2()
        {
            var max = (Orientation == Orientation.Vertical ? Width - SplitterWidth : Height - SplitterWidth) - Panel2MinSize;
            var min = (Orientation == Orientation.Vertical ? Width - SplitterWidth - _panel2MaxSize : Height - SplitterWidth - _panel2MaxSize);
            SplitterDistance = SplitterDistance == max ? min : max;
            Invalidate(SplitterRectangle);
        }

        public void Pop()
        {
            var max = Math.Max(0, Width - Panel2MinSize - SplitterWidth);
            var min = Math.Max(0, Panel1MinSize);
            if (SplitterWidth > (max - min))
            {
                SplitterDistance = Panel1MinSize;
                return;
            }

            if (SplitterResetDistance < 0)
            {
                switch (Orientation)
                {
                    case Orientation.Horizontal:
                        SplitterDistance = Math.Min(max, Math.Max(min, ClientSize.Height / 2 - SplitterWidth / 2));
                        break;
                    case Orientation.Vertical:
                        SplitterDistance = Math.Min(max, Math.Max(min, ClientSize.Width / 2 - SplitterWidth / 2));
                        break;
                }
            }
            else if (FixedPanel == FixedPanel.Panel1 || FixedPanel == FixedPanel.None)
            {
                SplitterDistance = Math.Min(max, Math.Max(min, SplitterResetDistance));
            }
            else
            {
                SplitterDistance =
                    Orientation == Orientation.Horizontal
                        ? Math.Min(max, Math.Max(min, ClientSize.Height - SplitterResetDistance))
                        : Math.Min(max, Math.Max(min, ClientSize.Width - SplitterResetDistance));
            }
            Invalidate(SplitterRectangle);
        }

        public void Pull()
        {
            var max = Math.Max(0, Width - Panel2MinSize - SplitterWidth);
            var min = Math.Max(0, Panel1MinSize);
            if (SplitterWidth > (max - min))
            {
                SplitterDistance = Panel1MinSize;
                return;
            }

            if (FixedPanel == FixedPanel.Panel1 || FixedPanel == FixedPanel.None)
            {
                SplitterDistance = Math.Min(max, Math.Max(min, Panel1MinSize));
            }
            else
            {
                switch (Orientation)
                {
                    case Orientation.Horizontal:
                        SplitterDistance = Math.Min(max, Math.Max(min, ClientSize.Height - Panel2MinSize));
                        break;
                    case Orientation.Vertical:
                        SplitterDistance = Math.Min(max, Math.Max(min, ClientSize.Width - Panel2MinSize));
                        break;
                }
            }
            Invalidate(SplitterRectangle);
        }

        #endregion
    }
}
